﻿using GanFeng.Forest.WebApi.Configs;
using GanFeng.Forest.WebApi.Exceptions;
using GanFeng.Forest.WebApi.Objects;
using hyjx.comm;
using hyjx.comm.Exceptions;
using hyjx.comm.Interfaces;
using Hyjx.GanFengLogger;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using S7.Net;
using System.Text.Json;

namespace GanFeng.Forest.WebApi.Controllers
{
    /// <summary>
    /// 稀释控制器
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class DilutionController : ApiControllerBase<DilutionController>
    {
        IS7Protocol? _plc;
        //readonly int _realTimeDataBlock; // 实时数据块
        //readonly int _realTimeStep; // 实时 Step
        //readonly int _realTimeCmd; // 实时 CMD

        //readonly int _materialWeight; // 物料加注重量
        //readonly int _waterRatioWeight; // 水理论加注重量
        //readonly string? _materialRecipeRatioAddress; // 配方物料配比地址
        //readonly string? _waterRecipeRatioAddress; // 配方水配比地址
        //readonly string? _waterWeightAddress; // 水实际加注重量地址
        private DilutionReadRealTime[]? _dilutionReadRealTimes;

        /// <summary>
        /// 创建一个新的稀释控制器
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="config"></param>
        public DilutionController(ILogger<DilutionController> logger, IConfiguration config) : base(logger, config)
        {
            //_materialRecipeRatioAddress = Config.GetValue<string>("Dilution:MaterialRecipeRatioAddress");
            //_waterRecipeRatioAddress = Config.GetValue<string>("Dilution:WaterR ecipeRatioAddress");
            //_waterWeightAddress = Config.GetValue<string>("Dilution:WaterWeightAddress");

            //_materialWeight = Config.GetValue<int>("RecipePoint:ReadRealTime:Dilution:MaterialWeight");
            //_waterRatioWeight = Config.GetValue<int>("RecipePoint:ReadRealTime:Dilution:WaterRatioWeight");

            //_realTimeDataBlock = Config.GetValue<int>("RecipePoint:ReadRealTime:Dilution:DataBlock");
            //_realTimeStep = Config.GetValue<int>("RecipePoint:ReadRealTime:Dilution:Step");
            //_realTimeCmd = Config.GetValue<int>("RecipePoint:ReadRealTime:Dilution:Cmd");

            _dilutionReadRealTimes = Config.GetSection("RecipePoint:ReadRealTime:Dilution").Get<DilutionReadRealTime[]>();
        }

        /// <summary>
        /// 设置工步运行，接口 Id: 222
        /// </summary>
        /// <param name="args">参数</param>
        /// <response code="200">执行完毕</response>
        [HttpPost(nameof(SetStepRun))]
        [ProducesResponseType(StatusCodes.Status200OK)]
        public async Task<ActionResult> SetStepRun(DilutionSetStepRunArgs args)
        {
            LogObject log = InitializeLog(args.DeviceCode, 222, args);
            try
            {
                PlcConfig device = GetDevicePlc(args.DeviceCode); // 拿到设备所属 PLC
                EnsureArgsAndCovertMaterialCode(args, device.FeatureType); // 确认参数

                // 稀释搅拌第一工步运行 == 备料（接口210）
                if (args.Step == 1)
                {
                    _plc = new S7Protocol(device.CpuType, device.Ip, device.Rack, device.Slot);

                    //TankConfig tank = GetFeedingTank(args.DeviceCode);
                    var tank = GetFeedingTankByMaterialCode(args.MaterialCode);
                    var valid = false;
                    for (int i = 0; i < 5; i++)
                    {
                        _plc.Write(tank.MesValidAddress, true); // 写 Mes 验证结果为 true
                        await Task.Delay(TimeSpan.FromMilliseconds(180));
                        valid = _plc.ReadBool(tank.MesValidAddress);
                        if (valid) break;
                    }
                    MesVisitSucceed += $"MES Valid Address:{tank.MesValidAddress},Value:{valid}";

                    if (Factory != 2 && Factory != 4)
                    {
                        await Task.Run(() =>
                        {
                            // 参数说明：
                            // 参数 1 表示要监控 Mes 校验设置功能
                            // 参数 2 表示 CPU 类型
                            // 参数 3 表示 CPU IP
                            // 参数 4 表示 CPU 机架
                            // 参数 5 表示 CPU 插槽
                            // 参数 6 表示物料 Mes 验证结果地址
                            // 参数 7 表示上料地址
                            // 参数 8 表示上料重量地址
                            // 参数 9 表示 MES 验证通过持续分钟数
                            // 参数 10 表示 WebApi 基本地址
                            // 参数 11 表示设备编码
                            // 参数 12 表示接口 ID
                            // 参数 13 表示物料编码
                            ExecuteFeedingSystemTimedTask($"2,{(int)device.CpuType},{device.Ip},{device.Rack},{device.Slot},{tank.MesValidAddress},{tank.FillingAddress},{tank.WeightAddress},{SystemSetting?.MesValidKeepMinutes},{SystemSetting?.MesApiBaseUrl},{args.DeviceCode},222,{args.MaterialCode}");
                        });
                    }

                    using StreamWriter writer = new($"{AppDataFolder}\\{args.DeviceCode}_recipe_step1_start");
                    writer.Write(args.Step);
                    writer.Close();
                }
                else if (args.Step == 2)
                {
                    using StreamWriter writer = new($"{AppDataFolder}\\{args.DeviceCode}_recipe_step2_start");
                    writer.Write(args.Step);
                    writer.Close();
                }

                return OkResult(log);
            }
            catch (EnsureArgsAndCovertMaterialCodeException ex)
            {
                return BadResult(ex.Code, log);
            }
            catch (GetDevicePlcException ex)
            {
                return BadResult(ex.Code, log);
            }
            catch (Exception ex)
            {
                int code = 6;
                switch (ex.Message)
                {
                    case nameof(S7ProtocolReadException):
                    case nameof(S7ProtocolWriteException):
                        code = 11;
                        break;
                    default:
                        break;
                }
                return ExceptionResult(code, ex, log);
            }
            finally
            {
                _plc?.Dispose();
            }
        }

        /// <summary>
        /// 稀释搅拌工步完成，接口 Id: 223
        /// </summary>
        /// <param name="args">参数</param>
        /// <response code="200">执行完毕</response>
        [HttpPost(nameof(SetStepFinish))]
        [ProducesResponseType(StatusCodes.Status200OK)]
        public async Task<ActionResult> SetStepFinish(SetStepFinishArgs args)
        {
            LogObject log = InitializeLog(args.DeviceCode, 223, args);
            PlcConfig device = GetDevicePlc(args.DeviceCode); // 拿到设备所属 PLC
            EnsureArgsAndCovertMaterialCode(args, device.FeatureType); // 确认参数

            if (args.Step != 1)
            {
                return OkResult(log);
            }

            // 下面走的是 step == 1 的逻辑，第一工步完成，表示 CNT 或者 LA133 加完
            _plc = new S7Protocol(device.CpuType, device.Ip, device.Rack, device.Slot);
            try
            {
                var dilutionRecipe = _dilutionReadRealTimes?.FirstOrDefault(r => r.DeviceCode == args.DeviceCode);
                if (dilutionRecipe is null) return BadResult(3, log);

                var _realTimeDataBlock = dilutionRecipe.DataBlock;
                var _realTimeStep = dilutionRecipe.Step;
                var _realTimeCmd = dilutionRecipe.Cmd;
                var _materialWeight = dilutionRecipe?.MaterialWeight;

                string stepAddress = $"DB{_realTimeDataBlock}.DBW{_realTimeStep}";
                string cmdAddress = $"DB{_realTimeDataBlock}.DBW{_realTimeCmd}";

                int step = _plc.ReadInt(stepAddress); // 读取 Step
                MesVisitFailed += $"PLC step:{step}";
                if (step != 1) // 下发配方失败，不允许执行上料完成
                    return BadResult(39, log);

                _plc.Write(cmdAddress, 1); // 写入 CMD = 1
                await Task.Delay(TimeSpan.FromSeconds(2));

                step = _plc.ReadInt(stepAddress); // 读取 Step
                if (step != 200)
                    return BadResult(41, log); // 机器开机异常，无法正确读取上料重量

                float weight = _plc.ReadFloat($"DB{_realTimeDataBlock}.DBD{_materialWeight}"); // 读取上料重量
                return BuildWithWeightResult(0, MesVisitSucceed, weight, log);
            }
            catch (EnsureArgsAndCovertMaterialCodeException ex)
            {
                return BadResult(ex.Code, log);
            }
            catch (GetDevicePlcException ex)
            {
                return BadResult(ex.Code, log);
            }
            catch (Exception ex)
            {
                int code = 6;
                switch (ex.Message)
                {
                    case nameof(S7ProtocolReadException):
                    case nameof(S7ProtocolWriteException):
                        code = 11;
                        break;
                    default:
                        break;
                }
                return ExceptionResult(code, ex, log);
            }
            finally
            {
                _plc?.Dispose();
            }
        }

        /// <summary>
        /// 稀释搅拌下发重量确认，接口 Id: 224
        /// </summary>
        /// <param name="args">参数</param>
        /// <response code="200">执行完毕</response>
        [HttpPost(nameof(ConfirmWeight))]
        [ProducesResponseType(StatusCodes.Status200OK)]
        public async Task<ActionResult> ConfirmWeight(ConfirmWeightArgs args)
        {
            LogObject log = InitializeLog(args.DeviceCode, 224, args);

            PlcConfig device = GetDevicePlc(args.DeviceCode); // 拿到设备所属 PLC
            EnsureArgsAndCovertMaterialCode(args, device.FeatureType); // 确认参数

            _plc = new S7Protocol(device.CpuType, device.Ip, device.Rack, device.Slot);
            try
            {
                var dilutionRecipe = _dilutionReadRealTimes?.FirstOrDefault(r => r.DeviceCode == args.DeviceCode);
                if (dilutionRecipe is null) return BadResult(3, log);

                var _realTimeDataBlock = dilutionRecipe.DataBlock;
                var _realTimeStep = dilutionRecipe.Step;
                var _realTimeCmd = dilutionRecipe.Cmd;
                var _materialWeight = dilutionRecipe.MaterialWeight;
                var _waterRatioWeight = dilutionRecipe.WaterRatioWeight;

                int step = 0;
                float weight = 0;
                string stepAddress = $"DB{_realTimeDataBlock}.DBW{_realTimeStep}";
                string cmdAddress = $"DB{_realTimeDataBlock}.DBW{_realTimeCmd}";
                switch (args.Type)
                {
                    case 2: // 确认加液重量
                        step = _plc.ReadInt(stepAddress); // 读取 Step
                        MesVisitFailed += $"PLC step:{step}";
                        if (step != 350) // step 不等于 350 
                            return BadResult(44, log); // 稀释搅拌加液未正常完成，请现场确认机器情况

                        _plc.Write(cmdAddress, 350); // CMD 写入 350

                        //if (step != 300) // step 不等于 300 
                        //    return BadResult(43, log); // 机器无法正常进入加液工步，请现场确认机器情况

                        //_plc.Write($"DB{_realTimeDataBlock}.DBD{_waterRatioWeight}", args.Weight); // 水理论加注重量重新写入一次
                        //_plc.Write(cmdAddress, 300); // CMD 写入 300

                        weight = (float)args.Weight;
                        return BuildWithWeightResult(0, MesVisitSucceed, weight, log); // 加水理论重量返回 MES
                    case 3: // 物料加注重量
                        step = _plc.ReadInt(stepAddress); // 读取 Step
                        MesVisitFailed += $"PLC step:{step}";
                        if (step != 200) // step 不等于 200 
                            return BadResult(42, log); // 机器开机异常，无法正确写入上料重量

                        _plc.Write($"DB{_realTimeDataBlock}.DBD{_materialWeight}", args.Weight); // 物料加注重量重新写入一次
                        _plc.Write(cmdAddress, 200); // CMD 写入 200
                        var cmdValue = _plc.ReadInt(cmdAddress);
                        MesVisitSucceed += $"CMD value:{cmdValue}";
                        await Task.Delay(TimeSpan.FromSeconds(3)); // 等待三秒
                        weight = _plc.ReadFloat($"DB{_realTimeDataBlock}.DBD{_waterRatioWeight}"); // 拿到理论配比加NMP重量
                        return BuildWithWeightResult(0, MesVisitSucceed, weight, log); // 理论配比加水重量返回 MES
                    default:
                        return BadResult(45, log);
                }
            }
            catch (Exception ex)
            {
                int code = 6;
                switch (ex.Message)
                {
                    case nameof(S7ProtocolReadException):
                    case nameof(S7ProtocolWriteException):
                        code = 14;
                        break;
                    default:
                        break;
                }
                return ExceptionResult(code, ex, log);
            }
            finally
            {
                _plc.Dispose();
            }
        }
    }
}
